#include <DefenseManager.h>
#include <BorderManager.h>

DefenseManager* TheDefenseManager = NULL;
  //////// BIDDING CONSTS TO WORK WITH OTHER MODULES
const int DefenseManager::BID_STANDBY = 20; // when standing still waiting to acquire targets
const int DefenseManager::BID_ACTIVE  = 70; // when heading into defensive combat
const int DefenseManager::BID_HEALING_RUN = 65; // when running away from a losing battle
const int DefenseManager::BID_HEALING_IDLE = 40; // can be taken away by MorphManager
//////////////////////////////////////////////////
DefenseManager* DefenseManager::create(Arbitrator::Arbitrator<BWAPI::Unit*,double>* arbitrator)
{
  if (TheDefenseManager == NULL) 
    TheDefenseManager = new DefenseManager(arbitrator);

  return TheDefenseManager;
}
void DefenseManager::destroy()
{
  if (TheDefenseManager)
    delete TheDefenseManager;
}
//////////////////

DefenseManager::DefenseManager(Arbitrator::Arbitrator<BWAPI::Unit*,double>* arbitrator)
{
  this->arbitrator = arbitrator;
  this->numActiveThreats = 0;
  this->missionCounter = 0;
}
void DefenseManager::setBorderManager(BorderManager* borderManager)
{
  this->borderManager=borderManager;
}

void DefenseManager::setBuildOrderManager (BuildOrderManager* builder){
  this->builder = builder;
}

void DefenseManager::onOffer(std::set<BWAPI::Unit*> units)
{
  for(std::set<BWAPI::Unit*>::iterator u = units.begin(); u != units.end(); u++)
  {
    if (defenders.find(*u) == defenders.end())
    {

      arbitrator->accept(this, *u);
      DefenseData temp;

      if ( isWounded(*u) && shouldEscape(*u) ){ 
        temp.mode = DefenseManager::DefenseData::HEALING;
        healingUnits.insert(*u);
        (*u)->move(BWTA::getStartLocation(BWAPI::Broodwar->self())->getPosition());
      }
      defenders.insert(std::make_pair(*u, temp));
    }
  }
}

void DefenseManager::onRevoke(BWAPI::Unit* unit, double bid)
{
  defenders.erase(unit);
  this->healingUnits.erase( unit);
}

BWTA::Chokepoint* DefenseManager::getChokePointClosestToBase (BWTA::BaseLocation* target){
  // direct the colonies toward the closest
  std::set<BWTA::Chokepoint*> chokePoints = target->getRegion()->getChokepoints();
  // TODO:we don't wanna build toward a chokepoint leading to one of our own bases
  // but rather toward the dark fog of war
  std::set<BWTA::Chokepoint*>::iterator itr;
  BWTA::Chokepoint* nearestChokePoint = *chokePoints.begin();

  for (itr = chokePoints.begin(); itr!= chokePoints.end(); ){
    std::pair<BWTA::Region*,BWTA::Region*> regions = (*itr)->getRegions();
    if (!TheBaseManager->hasMyBase( regions.first) || !TheBaseManager->hasMyBase( regions.second) )
      nearestChokePoint = *(itr++);
    else
      chokePoints.erase(itr++);
    }

  double minDistance = 99999999.0;
  
  
  for each (BWTA::Chokepoint* cp in chokePoints){
    double distance = cp->getCenter().getDistance(target->getPosition());
    if ( minDistance < distance ){
      minDistance = distance;
      nearestChokePoint = cp;
    }
  }
  return nearestChokePoint;
}

int DefenseManager::getNumActiveThreats(){
  return this->numActiveThreats;
}

// Creep Colonies set up first, then can be morphed later
// when reactive order comes in (from buildOrderManager)
void DefenseManager::buildDefensiveStructures (BWTA::BaseLocation* baseLoc, int priority){
  BWTA::Chokepoint* nearestChokePoint = getChokePointClosestToBase(baseLoc);
  int numBases  = TheBaseManager->getAllBases().size();
  int numBuild;
  if (numBases == 1)
    numBuild = 0; // skip first base -- focus on zerglings.
  else if (numBases < 3)
    numBuild = 3;
  else if (numBases < 5)
    numBuild = 2;
  else
    numBuild = 1;
  
  if (numBuild < 1) return;
  for (int i =0; i < numBuild; i++)
    builder->buildAdditional(1, BWAPI::UnitTypes::Zerg_Creep_Colony, priority,
    BWAPI::TilePosition ( nearestChokePoint->getCenter() ) );
  BWAPI::Broodwar->printf("Adding defensive structures [priority %d] ...", priority);
  BWAPI::Broodwar->pingMinimap(nearestChokePoint->getCenter());
}

// Called  by BaseManager whenever a new expansion has been completed.
// Delay the  build time to allow the creep to expand a bit
void DefenseManager::onExpansionCompleted(BWTA::BaseLocation* target, int priority){
  int currentFrame = BWAPI::Broodwar->getFrameCount();
  // the more expansion, the more leisure we can afford (give room for other units to build)
  int waitDuration = 350 + TheBaseManager->getAllBases().size() * 600;
  this->defensiveBuildSchedule[target] = std::pair<int,int>(currentFrame+waitDuration,priority);
}

// checks to  see if any build schedule is due
// and also use this chance to convert Creep Colonies into Sunken/Spore
// depending on the threats
void DefenseManager::updateDefensiveBuildSchedule (std::list<InformationManager::Threat> &activeThreats){
  int currentTime = BWAPI::Broodwar->getFrameCount();

  std::map<BWTA::BaseLocation*, std::pair<int, int> >::iterator itr;

  for (itr= defensiveBuildSchedule.begin(); itr!= defensiveBuildSchedule.end(); ) {
    if ( currentTime > (*itr).second.first) {
      // only build if the base is still there!
      if (TheBaseManager->hasMyBase( ( (*itr).first->getRegion())) )
        buildDefensiveStructures( (*itr).first, (*itr).second.second );
      defensiveBuildSchedule.erase(itr++);
    }
    else
      itr++;
  }
  if (activeThreats.size() == 0) return;
  
    // where is this threat most threatening for?
  UnitGroup group;
  for each (InformationManager::Threat threat in activeThreats) {
    
    UnitGroup next = UnitGroup::getUnitGroup(threat.getEnemies());
    group = group + next;
  }
  
  std::set<Base*> myBases = TheBaseManager->getAllBases();
  for each (Base* base in myBases){
    
    // now what do we face?
    int flyThreat = 0;
    int hideThreat = 0;
    int groundThreat = 0;
    
    
    std::set<BWAPI::Unit*> closestEnemies = group.onlyNearestBaseLocation(base->getBaseLocation());
    double distance = group.onlyNearestBaseLocation(base->getBaseLocation()).getCenter().getDistance(base->getBaseLocation()->getPosition());
    if (distance > 900) continue; // forget it.

    for each ( BWAPI::Unit* unit  in closestEnemies) {

      bool canFly = unit->getType().isFlyer() || unit->getType().isFlyingBuilding();
      bool canHide = unit->getType().isCloakable() || unit->getType().hasPermanentCloak();
      
      if (canFly) 
        flyThreat++;
      else
        groundThreat++;
      if (canHide) hideThreat++;
      
    }
    int total = 2*flyThreat + hideThreat + groundThreat;
    if (total == 0) continue;

    double sunkenRatio = (double) (groundThreat) / (total);
    // builds all Creep Colonies up
    std::set<BWAPI::Unit*> allCreepColonies = SelectAll(BWAPI::UnitTypes::Zerg_Creep_Colony)(isCompleted).onlyNearestBaseLocation(base->getBaseLocation());
    int buildTotal = allCreepColonies.size();
    if (buildTotal > 0){
      int sunkenCnt  = (int)( buildTotal *sunkenRatio);
      int sporeCnt = buildTotal - sunkenCnt;
      for each (BWAPI::Unit* colony in allCreepColonies){
        if (sunkenCnt-- > 0)
          colony->morph(BWAPI::UnitTypes::Zerg_Sunken_Colony);
        else if (sporeCnt-- >0 )
          colony->morph(BWAPI::UnitTypes::Zerg_Spore_Colony);
      }
    }
    
  }
}

// when a unit is destroyed. Handling unit losses is done during update.
void DefenseManager::onRemoveUnit(BWAPI::Unit* unit)
{

  this->healingUnits.erase(unit);
  defenders.erase(unit);
}



// returns a list of applicable Zerg units that can deal with the given threat
std::set<BWAPI::UnitType> DefenseManager::getSuitableUnitTypes (InformationManager::Threat &threat){
  std::set<BWAPI::UnitType> result;
  result.insert (BWAPI::UnitTypes::Zerg_Hydralisk);
  result.insert (BWAPI::UnitTypes::Zerg_Mutalisk);
  
  std::map<int, BWAPI::Unit*> *enemyUnits = &threat.enemyUnits;
  std::map<int, BWAPI::Unit*>::iterator iter;
  for (iter=enemyUnits->begin(); iter!=enemyUnits->end();iter++){
    // is this unit on the ground or on air?
    BWAPI::Unit* unit = iter->second;
    BWAPI::UnitType unitType = unit->getType();
    bool flyer = unitType.isFlyer();
    bool invisible = unit->isCloaked() || unit->isBurrowed();
    if (flyer){
      result.insert (BWAPI::UnitTypes::Zerg_Devourer);
      result.insert (BWAPI::UnitTypes::Zerg_Scourge);
    } else {
      result.insert (BWAPI::UnitTypes::Zerg_Zergling);
      result.insert (BWAPI::UnitTypes::Zerg_Lurker);
      result.insert (BWAPI::UnitTypes::Zerg_Ultralisk);
      result.insert (BWAPI::UnitTypes::Zerg_Guardian);
    }
    if (invisible) {
      result.insert (BWAPI::UnitTypes::Zerg_Overlord);
    }
  }
  return result;
                                            
}

// selects all RESERVE or STANBY defenders of appropriate type
// But spares HEALING units, which should be allowed to return
// Will consider the closest Overlord for cloak detection
std::set<BWAPI::Unit*> DefenseManager::getDefenders (std::set<BWAPI::UnitType> &types, InformationManager::Threat &threat){
  std::set<BWAPI::Unit*> result;
  BWAPI::Unit* overlord = NULL;
  double minOverlordDist = 99999999999;

  for (std::map<BWAPI::Unit*, DefenseData>::iterator iter = defenders.begin();
    iter != defenders.end(); iter++){

    BWAPI::Unit* unit = iter->first;

    if (iter->second.mode != DefenseData::ACTIVE && iter->second.mode != DefenseData::HEALING){
      if ( types.find(unit->getType()) != types.end()){
        if (unit->getType().getName().compare( "Zerg Overlord") == 0){
          double overlordDist = unit->getPosition().getDistance(threat.position);
          if (minOverlordDist > overlordDist) {
            overlord = unit;
            minOverlordDist = overlordDist;
          }
          continue; // we'll consider an Overlord at the end.
        }

        result.insert (unit);
        arbitrator->setBid(this, unit, BID_ACTIVE);
        defenders[unit].mode = DefenseData::ACTIVE;
      }
    }
  }
  // only sends an Overlord if there is sufficient units to guard it.
  if (result.size() > 0 && overlord != NULL){
    result.insert(overlord);
    arbitrator->setBid(this, overlord, BID_ACTIVE);
    defenders[overlord].mode = DefenseData::ACTIVE;
    BWAPI::Broodwar->printf("\tOverlord scrambling!");
  }
  return result;
}

// WARNING: this method is destructive, as it takes away from the given
// unit set all eligible units and returns those as the result.
std::set<BWAPI::Unit*> DefenseManager::getDefenders(
                    std::set<BWAPI::Unit*> &units, std::set<BWAPI::UnitType> &types, InformationManager::Threat &threat){
  std::set<BWAPI::Unit*> results;
  BWAPI::Unit* overlord = NULL;
  double minOverlordDist = 99999999999;

  std::set<BWAPI::Unit*>::iterator itr;
  for (itr = units.begin(); itr != units.end(); ){
    // removes healing units from this list. They shall be left aln
    if (defenders[(*itr)].mode  == DefenseData::HEALING ){
      units.erase(itr++);
      continue;
    }

    if ( types.find((*itr)->getType() ) != types.end() ) {
      if ((*itr)->getType().getName().compare( "Zerg Overlord") == 0){
        double overlordDist = (*itr)->getPosition().getDistance(threat.position);
        if (minOverlordDist > overlordDist) {
          overlord = *itr;
          minOverlordDist = overlordDist;
        }
        itr++;
        continue; // we'll consider an Overlord at the end.
      }
      results.insert( (*itr) );
      arbitrator->setBid(this, *itr, BID_ACTIVE);
      units.erase(itr++);
    }
    else{
      itr++;
    }
  }
  // only sends an Overlord if there is sufficient units to guard it.
  if (results.size() > 0 && overlord != NULL){
    results.insert(overlord);
    defenders[overlord].mode = DefenseData::ACTIVE;
    units.erase(overlord);
    arbitrator->setBid(this, overlord, BID_ACTIVE);
    BWAPI::Broodwar->printf("\tAlt: Overlord scrambling!");
  }
  return results;
}



// updates the status of the given defense mission's threat
// Note that the DefenseMission is passed by ref!
void DefenseManager::updateDefenseMissionStat (const InformationManager::Threat &threat, DefenseManager::DefenseMission &mission){

  std::map<int, BWAPI::Unit*> enemyUnits = threat.enemyUnits;
  std::map<int,BWAPI::Unit*>::const_iterator iter;
  bool atLeastOneEnemyAlive = false;
  // how are the bastards?
  for (iter=enemyUnits.begin(); iter!=enemyUnits.end(); iter++){
          BWAPI::Unit* unit = iter->second;
          if (unit->exists()) {
            // TONG: again, this is due to the bug/weird feature in BWAPI
            // that makes the pointer to a Refinery unit point to the vespene
            // geyser underneath it sometimes (always if the Refinery was placed 
            // using the map editor.)
            if ( unit->getType().isInvincible() )
              continue;
            atLeastOneEnemyAlive = true;
            break;
          }
  }
  
  if (!atLeastOneEnemyAlive) {
   
    mission.status = DefenseMission::SUCCESS;
    return;
  }
   
  // otherwise, still in action!
  //mission.lastActionFrame = BWAPI::Broodwar->getFrameCount();
  // how are the heroes?
  // TODO: experiment with the current measure of FAILING status
  std::set<BWAPI::Unit*>::iterator ourItr;
  bool gotIdleUnit = false; 
  for (ourItr = mission.units.begin(); ourItr != mission.units.end(); ){
    if ( !(*ourItr)->exists()) 
      mission.units.erase(ourItr++);
    else {
      if ( (*ourItr)->isIdle() ) gotIdleUnit = true;
      ourItr++;
    }
  }

 
  if (gotIdleUnit){ // cannot afford anyone slacking
    BWAPI::Broodwar->printf("Got idle units! %d", mission.id);
    mission.status = DefenseMission::STANDBY;
    return;
  }
  int survivors = mission.units.size();

  float survivalRatio = ( (float)survivors / mission.initStrength );
  if (survivors == 0)
    mission.status = DefenseMission::FAILED;
  else if (survivalRatio < 0.5f)
    mission.status = DefenseMission::FAILING;
  else
    mission.status = DefenseMission::ONGOING;
}

// Orders all units to attack-move toward given position
void DefenseManager::orderGroupAttack (const std::set<BWAPI::Unit*> &units, const BWAPI::Position &pos){
  std::set<BWAPI::Unit*>::const_iterator itr;
  for (itr = units.begin(); itr != units.end(); itr++){
    (*itr)->attack(pos);
  }
}

// Rebuilds a mission's attack force, number scaling with the ratio
// of current globla threat level vs that at the creation of the mission.
void DefenseManager::rebuildDefenseGroup (DefenseMission* mission){
  if (mission->rebuilt) {
    return; // no duplicate rebuild order!
  }
  double rebuildRatio = (float) numActiveThreats / mission->initStrength;
  BWAPI::Broodwar->printf("Restoring annihilated defense task force (size was %d)...", mission->initStrength);
  for each (std::pair<BWAPI::UnitType, int> p in  mission->initialComposition){
    this->builder->build( p.second, p.first, 600 + (int) (100*rebuildRatio));
  }
  mission->rebuilt = true;
}




bool DefenseManager::isHealed (BWAPI::Unit* unit){
  int maxHitPoints = unit->getType().maxHitPoints();
  if (maxHitPoints == 0) return false;
  return (  ((float) unit->getHitPoints() / maxHitPoints) > 0.8 );
}

bool DefenseManager::isWounded (BWAPI::Unit* unit){

  int maxHitPoints = unit->getType().maxHitPoints();
  if (maxHitPoints == 0) return false;
int currHitPoints = unit->getHitPoints();
double ratio =  ( (float) currHitPoints / maxHitPoints );
  return ratio < 0.4;

}

// gives the damage multiplier for the attacker's weapon on the target
double DefenseManager::getDamageMultiplier (BWAPI::Unit* attacker, BWAPI::Unit* target){
  
  BWAPI::UnitType targetType = target->getType();
  BWAPI::UnitSizeType targetSize = targetType.size();

  BWAPI::UnitType attackerType = attacker->getType();
  BWAPI::WeaponType attackWeapon;
  if (targetType.isFlyer() )
    attackWeapon = attackerType.airWeapon();
  else
    attackWeapon = attackerType.groundWeapon();

  if (attackWeapon == BWAPI::WeaponTypes::None)
    return 0;

  double multiplier = 1;
  BWAPI::DamageType attackDamageType = attackWeapon.damageType();
  if (attackDamageType== BWAPI::DamageTypes::Concussive){
      if (targetSize == BWAPI::UnitSizeTypes::Small)
        multiplier = 1;
      else if (targetSize == BWAPI::UnitSizeTypes::Medium)
        multiplier = 0.5;
      else if (targetSize == BWAPI::UnitSizeTypes::Large)
        multiplier = 0.25;
      else
        multiplier = 1; // funky
  }
  else if (attackDamageType == BWAPI::DamageTypes::Explosive){
      if (targetSize == BWAPI::UnitSizeTypes::Small)
        multiplier = 0.5;
      else if (targetSize == BWAPI::UnitSizeTypes::Medium)
        multiplier = 0.75;
      else if (targetSize == BWAPI::UnitSizeTypes::Large)
        multiplier = 1;
      else
        multiplier = 1; // funky
  }
  else if (attackDamageType == BWAPI::DamageTypes::Normal){
        multiplier = 1;
  } 
  else
        multiplier = 1;
  return multiplier;
}


BWAPI::WeaponType DefenseManager::getAppropriateWeapon(BWAPI::Unit* attacker, BWAPI::Unit* target){
  BWAPI::WeaponType weapon;
    if (target->getType().isFlyer())
      weapon = attacker->getType().airWeapon();
    else
      weapon = attacker->getType().groundWeapon();
    // depending on the size
    return weapon;
}


// sees if this unit can survive a mad dash away from surrounding given enemies
// Takes into account friendly mobile units that can share the damage
bool DefenseManager::shouldEscape (BWAPI::Unit* unit){
  // tally active enemy damage output, filtered by relevance
  double totalScaledDamage = 0;
  int hp                   = unit->getHitPoints();
  double runningTime       = 0;
  
  double dpf               = 0; // Damage per Frame
  BWAPI::UnitType type = unit->getType();

  for each (BWAPI::Unit* entity in unit->getUnitsInRadius(type.sightRange() ) ) {
    if (!BWAPI::Broodwar->self()->isEnemy(entity->getPlayer())) {
      continue;
    }
    BWAPI::WeaponType entityWeapon = getAppropriateWeapon(entity, unit);
    int effectiveDamage = (int) (getDamageMultiplier(entity, unit) * entityWeapon.damageAmount());
    int entityWeaponCoolDown = entityWeapon.damageCooldown(); // frame
    
    if (entityWeapon == BWAPI::WeaponTypes::None) continue; 
    // is this unit targeting me? If not then don't bother. 
    // TODO: take into account splash damage too! Such is life...
    if (entity->getOrderTarget() != unit) {
      continue;
    }
    // running distance till we're out of range
    int entWeaponRange = entityWeapon.maxRange();
    double distance    = entity->getPosition().getDistance(unit->getPosition());
    double runningDist =  distance - entWeaponRange; // pixel?
    if (runningDist > 0) continue;
    
    dpf += ((double) effectiveDamage) / entityWeaponCoolDown;

    double speed   = type.topSpeed(); // pixel/frame
    int numChasingShots   = (int) floor (  (-runningDist/speed)/  entityWeaponCoolDown + 0.5);

  

    totalScaledDamage  += effectiveDamage * numChasingShots;
  }
  // now go through the enemy units again and see if we should stay and fight
  // a last stand for the greater good.
  for each (BWAPI::Unit* entity in unit->getUnitsInRadius(type.sightRange() ) ) {
    if (!BWAPI::Broodwar->self()->isEnemy(entity->getPlayer())) {
      continue;
    }
    // how much time we have to live?
    int timeLeft = (int) floor ( hp / dpf + 0.5);
    // We may make a last stand! ///////////////////////////////////
    BWAPI::WeaponType myWeapon = getAppropriateWeapon(unit, entity);
    if (myWeapon != BWAPI::WeaponTypes::None){
      bool worthIt = ( type.mineralPrice() < entity->getType().mineralPrice() ||
                          type.gasPrice() < entity->getType().gasPrice() );
      if (worthIt) {
        BWAPI::WeaponType entityWeapon = getAppropriateWeapon(entity, unit);
        // if within the unit's minimum fire range, then by all means stay!
        if (entityWeapon.minRange() > 0)  // means it's a siege tank
          return false;// go for it!

        // otherwise, can I kill it in time?
        // if it can fight back, i need to be careful and take care of myself,
        // because if there are others around they'll fight on anyway.
        // if it doesn't attack me, go for it!
        if (entityWeapon == BWAPI::WeaponTypes::None)
          return false; // free kill, relatively speaking

        int myEffectiveDamage = (int) (getDamageMultiplier(unit, entity) * myWeapon.damageAmount());
        int entityHP = entity->getHitPoints();
        
        int numShots = timeLeft / (myWeapon.damageCooldown() );

        if (numShots * myEffectiveDamage > entityHP )
          return false; // try my best!
      }
    }
  }
  return ( totalScaledDamage < hp );
}

// little function to sort a target list first by the effective HP
// and then by the target's unique ID.
bool compColonyTargets (std::pair<double, BWAPI::Unit*> a,
                                     std::pair<double, BWAPI::Unit*> b ) {
  if (a.first < b.first)
    return true;
  else if (a.first > b.first)
    return false;
  else 
    return  (a.second->getID() < b.second->getID() );
}

/**
  Enables coordination of focus-fire for sunken and spore colonies.
  Algorithm: first collects the list of targets in attack range of all colonies,
  then calculates, for each target, the number of sunken hits/spore hits that 
  will bring it down
  then divides that value by the number of colonies that can simultaneously attack this target
  then sorts the units by this value, and then by unitID to break even AND to allow focus fire on the same unit to still work.
  Go through the units in the sorted order above, order ALL applicable colonies
  to attack each of them, until there is no more colony
*/
void DefenseManager::updateStaticDefenseFocusFire() {
  // air units will yield a weaponTypes::None for sunken colony,
  // and vice versa, when parsed through the getAppropriateWEapon() method.
  // They will be screened out right then.
  std::set<BWAPI::Unit*> colonies = (SelectAll(BWAPI::UnitTypes::Zerg_Sunken_Colony) + SelectAll(BWAPI::UnitTypes::Zerg_Spore_Colony));
  
  // a map to keep track of the colonies capable of attacking a given target
  std::map<BWAPI::Unit*, std::set<BWAPI::Unit*> > targetToColonies;

  // and a map to remember how many shots from the appropriate colony type
  // will bring a given target down
  std::map<BWAPI::Unit*, int> targetToNumShots;


  for each (BWAPI::Unit* colony in colonies){
    BWAPI::WeaponType weapon;
    BWAPI::UnitType   type = colony->getType();
    if (type == BWAPI::UnitTypes::Zerg_Sunken_Colony)
      weapon = type.groundWeapon();
    else
      weapon = type.airWeapon();

    std::set<BWAPI::Unit*> targets = colony->getUnitsInWeaponRange(weapon);

    for each (BWAPI::Unit* target in targets){
      if (!BWAPI::Broodwar->self()->isEnemy(target->getPlayer())) {
      continue;
    }
      if (target->getType().isFlyer() ^ weapon.targetsAir() )
        continue;

      // only need to compute number of shots to kill this target once
      if (targetToNumShots.find(target) == targetToNumShots.end() ){
        double dmg   = getDamageMultiplier(colony, target) * weapon.damageAmount();
        int numShots = (int) 
          (floor( target->getShields()/weapon.damageAmount() + target->getHitPoints()/dmg + 0.5));
        targetToNumShots[target] = numShots;
      }
      // now account for the fact that the current colony can attack this unit
      targetToColonies[target].insert(colony);
    }
  }

  // now process the data:
  // 1) for each target, divide its number of shots by the number of colonies
  // able to attack this. This is the "effective hitpoints".
  // THen insert tuple (target, effectiveHP) into a vector
  std::vector<std::pair<double, BWAPI::Unit*> > targetList;
  for each (std::pair<BWAPI::Unit*, int> p in targetToNumShots ){
    double effectiveHP = ((double) p.second) / targetToColonies[p.first].size();
    targetList.push_back( std::make_pair(effectiveHP, p.first) );
  }
  // 2) Now sort the target list
  sort(targetList.begin(), targetList.end(), compColonyTargets );

  // 3) finally, order the colonies to attack the targets in the sorted order
  std::vector<std::pair<double, BWAPI::Unit*> >::iterator itr;
  for (itr = targetList.begin(); itr != targetList.end(); itr++ ){
    BWAPI::Unit* target = (*itr).second;

    for each (BWAPI::Unit* colony in targetToColonies[target]){
      if (colonies.find( colony ) == colonies.end() ) 
        // this means the colony is already attacking another target
        continue;

      colony->attack(target);
      // this colony is now busy, so leave them alone
      colonies.erase( colonies.find(colony) );
    }
          
    if (colonies.size() == 0 ) 
      // this means we have covered all availabel colonies
      break;
  }
}


 // There are two categories of defenders: reserve group and active response group
  // The reserve group will go to chokepoint for now, while the active group
  // is then pulled from chokepoints and newly created units to concentrate attack
  // on known units. Destroyed active groups will immediately request reinforcement
  // from the reserve group. However, depleted reserve group will be relying on
  // efficient rebuild and prioritization of buildORderManager to replenish themselves.
void DefenseManager::update()
{

  // pulls healed units back into the battlefield

  // First off: intelligence! 
  // Gathers intel on all detected enemy units close to our base(s) every second
  std::set<BWAPI::Unit*>::iterator itr;

  for  (itr = healingUnits.begin(); itr != healingUnits.end(); ){
    if  (this->isHealed(*itr) ){
      
      this->defenders[*itr].mode = DefenseManager::DefenseData::RESERVE;
      BWAPI::Broodwar->printf("%s back to defense duty.", (*itr)->getType().getName().c_str());
      arbitrator->setBid(this, *itr, BID_STANDBY);
      healingUnits.erase(itr++);
    }
    else{
      // if this unit is idle, set its bid level to HEALING so that
      // MorphManager can take over (this assumes the unit has run back safely)
      if ( (*itr)->isIdle())
        arbitrator->setBid(this, *itr, BID_HEALING_IDLE);
      itr++;
    }
  }


  if ( BWAPI::Broodwar->getFrameCount() % 24 == 0) {
    
    std::list<InformationManager::Threat> activeThreats = 
                                        TheInformationManager->getCurrentActiveThreats();
    this->numActiveThreats = activeThreats.size();
    
    // see if the sunken/spore colonies can do some focus fire kickass
    if (numActiveThreats > 0)
      updateStaticDefenseFocusFire();

      // time to build some more Creep Colonies? :D
    // will also get existing Creep Colonies and turn them into appropriate
    // defenses based on threat

    updateDefensiveBuildSchedule(activeThreats);
    // go through each active threat to update our threat records' statuses
    std::list<InformationManager::Threat>::iterator iter;
    for (iter = activeThreats.begin(); iter != activeThreats.end(); iter++){
      InformationManager::Threat threat = *iter;
     
      // new threat! xD
      if (this->threatDefenseMap.find(threat) == this->threatDefenseMap.end()){
        //BWAPI::Broodwar->printf("New threat: %s:", ((std::string) threat).c_str());
        std::set<BWAPI::UnitType> unitTypes = this->getSuitableUnitTypes(threat);
        std::set<BWAPI::Unit*> units = this->getDefenders(unitTypes, threat);
        if (units.size() > 0){
          threatDefenseMap[threat] = DefenseMission(this->missionCounter++, threat.position, units, numActiveThreats);
        }
      } else {
        // update enemy locations
        threatDefenseMap[threat].location = threat.position;
      }
    }

    // now go through all recorded threats and deal with each appropriately
    std::map<InformationManager::Threat, DefenseManager::DefenseMission>::iterator threatItr;
    std::set<BWAPI::Unit*>::iterator itr;
    this->numRecordedThreats = threatDefenseMap.size();
    for (threatItr = threatDefenseMap.begin(); threatItr != threatDefenseMap.end(); ){
      InformationManager::Threat threat = threatItr->first;
      if (threatDefenseMap.find(threat) == threatDefenseMap.end()){
        BWAPI::Broodwar->printf("Insidious insertion! threat = %s", ((std::string)threat).c_str());
        
      }

      DefenseManager::DefenseMission &mission = threatDefenseMap[threat];

      this->updateDefenseMissionStat(threat, mission); 
      int count = 0; // number of reinforcements

      switch (mission.status){
        // newly assembled to deal with threat, or threat has moved on to a different
        // location!
        case DefenseMission::STANDBY:
          // TODO: replace with Engage?
          this->orderGroupAttack(mission.units, mission.location);
          BWAPI::Broodwar->printf("ATTN: [%d] engaging threat %s with %d units!",
            mission.id, ((std::string)threat).c_str(), mission.units.size() );
          threatItr++;
          break;

        case DefenseMission::ONGOING:
          // TODO: reinforce this mission if the task force reports superior enemy units at this time (thus going to FAILING soon)
          threatItr++;
          break;

        case DefenseMission::SUCCESS:
          
          for each (InformationManager::Threat newThreat in activeThreats) {
            // we are only looking at mission that are already ongoing
            if ( threatDefenseMap.find(newThreat) == threatDefenseMap.end())
              continue;
            if (!(newThreat < threat) && !(threat < newThreat) ) {
              continue;
            }
            // selects another threat and its corresponding defense mission
            DefenseMission &newMission = threatDefenseMap[newThreat];
            newMission.location = newThreat.position;
            // with each extraction, the mission.units set gets smaller
            count = newMission.receiveReinforcements(getDefenders(mission.units,
            this->getSuitableUnitTypes(newThreat), threat) );

            if (count > 0){
              BWAPI::Broodwar->printf("\t%d joining another task force attacking at %d, %d",count, newMission.location.x(), newMission.location.y());
              // share the current mission for all new members
              this->orderGroupAttack(newMission.units, newMission.location);
              BWAPI::Broodwar->pingMinimap(newMission.location);
            }
          }

          
    // the rest of the units can go back
          for (itr=mission.units.begin(); itr!=mission.units.end();itr++){
            defenders[(*itr)].mode = DefenseManager::DefenseData::RESERVE;
            arbitrator->setBid(this, *itr, BID_STANDBY);

          }
          //BWAPI::Broodwar->printf("GREAT! Released task force units back to reserve.");
          BWAPI::Broodwar->printf("[%d] success!", mission.id);
          threatDefenseMap.erase(threatItr++);
          
          break;
        
        case DefenseMission::FAILING:
          // Should try to finish off one threat at a time, I think...
          rebuildDefenseGroup(& mission);
          count = mission.receiveReinforcements(this->getDefenders(
                                              this->getSuitableUnitTypes(threat), threat));
          if (count > 0){
            BWAPI::Broodwar->printf("[%d]Reinforcing task force units with %d extra.",mission.id, count);
            // share the current mission for all new members
            this->orderGroupAttack(mission.units, mission.location);
            BWAPI::Broodwar->pingMinimap(mission.location);
          }
          threatItr++;
        break;
        
        case DefenseMission::FAILED:
          // the next availble reserve group shall be pulled into use soon.
          // to help, also request to rebuilt group.
          threatDefenseMap.erase(threatItr++); // another group will continue the fight
        break;
      } 
      
    }
  }

  
  std::stringstream ss;
  for each (std::pair<InformationManager::Threat, DefenseMission> p in threatDefenseMap)
    ss << p.second.id << " " ;
  
  BWAPI::Broodwar->drawTextScreen (5,64, "Active threats %d" , this->numActiveThreats);
  BWAPI::Broodwar->drawTextScreen (5, 160, "Recorded threats %d BUT really %d {%s}" , this->numRecordedThreats, this->threatDefenseMap.size(), ss.str().c_str());
  BWAPI::Broodwar->drawTextScreen (5, 96, "Healthy Mutalisks %d" , TheInformationManager->getHealthyMutasCount());

  BWAPI::Broodwar->drawTextScreen (5, 112, "Healing Units %d" , healingUnits.size());
  // Bid on all completed military units 
  // Whoever not doing something useful will have to go guard the posts
  std::set<BWAPI::Unit*> myPlayerUnits=BWAPI::Broodwar->self()->getUnits();
  for (std::set<BWAPI::Unit*>::iterator u = myPlayerUnits.begin(); u != myPlayerUnits.end(); u++)
  {
    if ((*u)->isCompleted() && 
        !(*u)->getType().isWorker() && 
        !(*u)->getType().isBuilding() &&
        (*u)->getType() != BWAPI::UnitTypes::Zerg_Egg &&
        (*u)->getType() != BWAPI::UnitTypes::Zerg_Larva )
    {
      // we already own these healing units so skip over.
      if (healingUnits.find(*u) != healingUnits.end() ) continue;  

      if ( isWounded(*u) && shouldEscape(*u)  ) {
        arbitrator->setBid(this, *u, DefenseManager::BID_HEALING_RUN); // monopolizes this unit for healing -- run first, once reaching base it will be set down to BID_HEALING_IDLE
        // but if it's already in Defense role, just go ahead
        if (defenders.find(*u) != defenders.end() ){
          healingUnits.insert(*u);
          Base* closest = TheBaseManager->getClosestActiveBase((*u)->getPosition());
          if (closest) {
            (*u)->move(  closest->getBaseLocation()->getPosition()  );
          }
          defenders[*u].mode = DefenseData::HEALING;
        }        
      } else if (defenders.find(*u) == defenders.end() )
        // only bid standby on units not already inside defenders' rank
        arbitrator->setBid(this, *u, BID_STANDBY);
    }
  }
  bool borderUpdated=false;
  if (myBorder!=borderManager->getMyBorder())
  {
    myBorder=borderManager->getMyBorder();
    myBorderVector.clear();
    for(std::set<BWTA::Chokepoint*>::iterator i=myBorder.begin();i!=myBorder.end();i++)
      myBorderVector.push_back(*i);
    borderUpdated=true;
  }

 

  //Order all RESERVE units to choke
  int i=0;
  if (!myBorder.empty())
  {
    for (std::map<BWAPI::Unit*,DefenseData>::iterator u = defenders.begin(); u != defenders.end(); u++)
    {
      if ((*u).second.mode == DefenseData::RESERVE || 
        (borderUpdated && ( (*u).second.mode == DefenseData::RESERVE ||
        (*u).second.mode == DefenseData::STANDBY )) )
      {
        
        //BWAPI::Position chokePosition=myBorderVector[i]->getCenter();
        BWAPI::Position chokePosition = getRandomChokePointPos(myBorderVector[i]);
        if (chokePosition == BWAPI::Positions::None) continue;
        i++;
        if (i>=(int)myBorderVector.size())
          i=0;
        
        (*u).first->attack(chokePosition);
        u->second.mode = DefenseData::STANDBY;
      }
    }
  }
}

// Given a chokepoint, returns a point about 1/2 the width of the chokepoint
// away from that chokepoint's center, such that this point is on the same
// side as one of our base(s) in the region containing the chokepoint (if any.)
// If this point is further than 500 units from the baseLocation, go there instead.
BWAPI::Position DefenseManager::getRandomChokePointPos (BWTA::Chokepoint* chokePoint) {
  BWTA::Region* region1 = chokePoint->getRegions().first;
  BWTA::Region* region2 = chokePoint->getRegions().second;
  BWTA::Region* baseRegion = NULL;
  if (TheBaseManager->hasMyBase(region1) ) 
    baseRegion = region1;
  else if (TheBaseManager->hasMyBase(region2) ) 
    baseRegion = region2;

  
  BWAPI::Position center = chokePoint->getCenter();
  double radius = (chokePoint->getWidth() / 2.0);
  // get vector point from this center toward the base
  // but if base has been destroyed, cancel this action
  if (baseRegion == NULL ){
    BWAPI::Broodwar->printf("Aborting choking order -- no base.");
    return BWAPI::Positions::None;
  }
  BWAPI::Position baseCenter =TheBaseManager->getMyBase(baseRegion)->getBaseLocation()->getPosition();
  BWAPI::Position chokePointToBaseVector = baseCenter - center;

  int finalX, finalY;
  if (chokePointToBaseVector.getLength() < 300 ) {
    // now the space is close enough that we actually may get into trouble
    // if we crowd abuot the foot of a hill. CHeck for this: if the other side
    // of the chokepoint is higher, go there. Else stay.
    BWAPI::Position outsidePoint = center - BWAPI::Position(chokePointToBaseVector.x()/4,chokePointToBaseVector.y()/4);
    if ( BWAPI::Broodwar->getGroundHeight(BWAPI::TilePosition(outsidePoint)) > BWAPI::Broodwar->getGroundHeight(BWAPI::TilePosition(baseCenter))){
      finalX = center.x()+ (rand() % 10 + 40) * (int)(- chokePointToBaseVector.x() / 100.0);
      finalY = center.y() + (rand() % 10 + 40) * (int)(- chokePointToBaseVector.x() / 100.0);
    }
    else { // even or lower, so stay behind chokepoint then.
      finalX = center.x()+ (rand() % 10 + 40) * (int)( chokePointToBaseVector.x() / 100.0);
      finalY = center.y() + (rand() % 10 + 40) * (int)( chokePointToBaseVector.x() / 100.0);
      
    }

  }
  else if (  chokePointToBaseVector.getLength() > 1000 ){
    //  extreme cases -- units now scatter around the base
    finalX = baseCenter.x() +  (rand() % 20 + 10) * (int)(- chokePointToBaseVector.x() / 100.0);
    finalY = baseCenter.y() +  (rand() % 20 + 10) * (int)(- chokePointToBaseVector.y() / 100.0);
  } else { 
    // keep a distance from 10-50% of this vector, away from the chokepoint and toward base
    finalX = center.x() +  (rand() % 4 + 1) * (int)( chokePointToBaseVector.x() / 10.0);
    finalY = center.y() +  (rand() % 4 + 1) * (int)( chokePointToBaseVector.y() / 10.0);
  }
  return BWAPI::Position(finalX, finalY).makeValid();
}

std::string DefenseManager::getName() const
{
  return "Defense Manager";
}

std::string DefenseManager::getShortName() const
{
  return "Def";
}
